//
// Copyright (C) 2004-2006 Andras Varga
// Copyright (C) 2000 Institut fuer Telematik, Universitaet Karlsruhe
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program; if not, see <http://www.gnu.org/licenses/>.
//


//  Cleanup and rewrite: Andras Varga, 2004

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <algorithm>
#include <sstream>
#include <map>

#include "RoutingTable.h"
#include "RoutingTableParser.h"
#include "IPRoute.h"
#include "IPv4InterfaceData.h"
#include "IInterfaceTable.h"
#include "InterfaceTableAccess.h"
#include "NotifierConsts.h"


Define_Module(RoutingTable);

std::ostream& operator<<(std::ostream& os, const IPRoute& e)
{
    os << e.info();
    return os;
};

RoutingTable::RoutingTable()
{
}

RoutingTable::~RoutingTable()
{
    for (unsigned int i=0; i<routes.size(); i++)
        delete routes[i];
    for (unsigned int i=0; i<multicastRoutes.size(); i++)
        delete multicastRoutes[i];
}

void RoutingTable::initialize(int stage)
{
    if (stage==0)
    {
        // get a pointer to the NotificationBoard module and IInterfaceTable
        nb = NotificationBoardAccess().get();
        ift = InterfaceTableAccess().get();

        IPForward = par("IPForward").boolValue();
        threshold = par("threshold").doubleValue();
        ASSERT(threshold > 0 && threshold < 1);

        nb->subscribe(this, NF_INTERFACE_CREATED);
        nb->subscribe(this, NF_INTERFACE_DELETED);
        nb->subscribe(this, NF_INTERFACE_STATE_CHANGED);
        nb->subscribe(this, NF_INTERFACE_CONFIG_CHANGED);
        nb->subscribe(this, NF_INTERFACE_IPv4CONFIG_CHANGED);

        WATCH_PTRVECTOR(routes);
        WATCH_PTRVECTOR(multicastRoutes);
        WATCH(IPForward);
        WATCH(routerId);
        isRouter_ = false;
        isEdge_ = false;
        srand(time(NULL)); // initialize the random generator
    }
    else if (stage==1)
    {
        // L2 modules register themselves in stage 0, so we can only configure
        // the interfaces in stage 1.
        const char *filename = par("routingFile");

        // At this point, all L2 modules have registered themselves (added their
        // interface entries). Create the per-interface IPv4 data structures.
        IInterfaceTable *interfaceTable = InterfaceTableAccess().get();
        for (int i=0; i<interfaceTable->getNumInterfaces(); ++i)
            configureInterfaceForIPv4(interfaceTable->getInterface(i));
        configureLoopbackForIPv4();

        // read routing table file (and interface configuration)
        RoutingTableParser parser(ift, this);
        if (*filename && parser.readRoutingTableFromFile(filename)==-1)
            error("Error reading routing table file %s", filename);

        // set routerId if param is not "" (==no routerId) or "auto" (in which case we'll
        // do it later in stage 3, after network configurators configured the interfaces)
        const char *routerIdStr = par("routerId").stringValue();
        if (strcmp(routerIdStr, "") && strcmp(routerIdStr, "auto"))
            routerId = IPAddress(routerIdStr);
    }
    else if (stage==3)
    {
        // routerID selection must be after stage==2 when network autoconfiguration
        // assigns interface addresses
        configureRouterId();

        // we don't use notifications during initialize(), so we do it manually.
        // Should be in stage=3 because autoconfigurator runs in stage=2.
        updateNetmaskRoutes();

        //printRoutingTable();
    }

}

void RoutingTable::configureRouterId()
{
    if (routerId.isUnspecified())  // not yet configured
    {
        const char *routerIdStr = par("routerId").stringValue();
        if (!strcmp(routerIdStr, "auto"))  // non-"auto" cases already handled in stage 1
        {
            // choose highest interface address as routerId
            for (int i=0; i<ift->getNumInterfaces(); ++i)
            {
                InterfaceEntry *ie = ift->getInterface(i);
                if (!ie->isLoopback() && ie->ipv4Data()->getIPAddress().getInt() > routerId.getInt())
                    routerId = ie->ipv4Data()->getIPAddress();
            }
        }
    }
    else // already configured
    {
        // if there is no interface with routerId yet, assign it to the loopback address;
        // TODO find out if this is a good practice, in which situations it is useful etc.
        if (getInterfaceByAddress(routerId)==NULL)
        {
            InterfaceEntry *lo0 = ift->getFirstLoopbackInterface();
            lo0->ipv4Data()->setIPAddress(routerId);
            lo0->ipv4Data()->setNetmask(IPAddress::ALLONES_ADDRESS);
        }
    }
}

void RoutingTable::updateDisplayString()
{
    if (!ev.isGUI())
        return;

    char buf[80];
    if (routerId.isUnspecified())
        sprintf(buf, "%d+%d routes", routes.size(), multicastRoutes.size());
    else
        sprintf(buf, "routerId: %s\n%d+%d routes", routerId.str().c_str(), routes.size(), multicastRoutes.size());
    getDisplayString().setTagArg("t",0,buf);
}

void RoutingTable::handleMessage(cMessage *msg)
{
    opp_error("This module doesn't process messages");
}

void RoutingTable::receiveChangeNotification(int category, const cPolymorphic *details)
{
    if (simulation.getContextType()==CTX_INITIALIZE)
        return;  // ignore notifications during initialize

    Enter_Method_Silent();
    printNotificationBanner(category, details);

    if (category==NF_INTERFACE_CREATED)
    {
        // add netmask route for the new interface
        updateNetmaskRoutes();
    }
    else if (category==NF_INTERFACE_DELETED)
    {
        // remove all routes that point to that interface
        InterfaceEntry *entry = check_and_cast<InterfaceEntry*>(details);
        deleteInterfaceRoutes(entry);
    }
    else if (category==NF_INTERFACE_STATE_CHANGED)
    {
        invalidateCache();
    }
    else if (category==NF_INTERFACE_CONFIG_CHANGED)
    {
        invalidateCache();
    }
    else if (category==NF_INTERFACE_IPv4CONFIG_CHANGED)
    {
        // if anything IPv4-related changes in the interfaces, interface netmask
        // based routes have to be re-built.
        updateNetmaskRoutes();
    }
}

void RoutingTable::deleteInterfaceRoutes(InterfaceEntry *entry)
{
    RouteVector::iterator it = routes.begin();
    while (it != routes.end())
    {
        IPRoute *route = *it;
        if (route->getInterface() == entry)
        {
            deleteRoute(route);
            it = routes.begin();  // iterator became invalid -- start over
        }
        else
        {
            ++it;
        }
    }
}

void RoutingTable::invalidateCache()
{
    routingCache.clear();
    localAddresses.clear();
}

void RoutingTable::printRoutingTable() const
{
    EV << "-- Routing table --\n";
    ev.printf("%-16s %-16s %-16s %-3s %s\n",
              "Destination", "Gateway", "Netmask", "Iface");

    for (int i=0; i<getNumRoutes(); i++)
        EV << getRoute(i)->detailedInfo() << "\n";
    EV << "\n";
}

std::vector<IPAddress> RoutingTable::gatherAddresses() const
{
    std::vector<IPAddress> addressvector;

    for (int i=0; i<ift->getNumInterfaces(); ++i)
        addressvector.push_back(ift->getInterface(i)->ipv4Data()->getIPAddress());
    return addressvector;
}

//---

void RoutingTable::configureInterfaceForIPv4(InterfaceEntry *ie)
{
    IPv4InterfaceData *d = new IPv4InterfaceData();
    ie->setIPv4Data(d);

    // metric: some hints: OSPF cost (2e9/bps value), MS KB article Q299540, ...
    d->setMetric((int)ceil(2e9/ie->getDatarate())); // use OSPF cost as default
}

InterfaceEntry *RoutingTable::getInterfaceByAddress(const IPAddress& addr) const
{
    Enter_Method("getInterfaceByAddress(%u.%u.%u.%u)", addr.getDByte(0), addr.getDByte(1), addr.getDByte(2), addr.getDByte(3)); // note: str().c_str() too slow here

    if (addr.isUnspecified())
        return NULL;
    for (int i=0; i<ift->getNumInterfaces(); ++i)
    {
        InterfaceEntry *ie = ift->getInterface(i);
        if (ie->ipv4Data()->getIPAddress()==addr)
            return ie;
    }
    return NULL;
}


void RoutingTable::configureLoopbackForIPv4()
{
    InterfaceEntry *ie = ift->getFirstLoopbackInterface();

    // add IPv4 info. Set 127.0.0.1/8 as address by default --
    // we may reconfigure later it to be the routerId
    IPv4InterfaceData *d = new IPv4InterfaceData();
    d->setIPAddress(IPAddress::LOOPBACK_ADDRESS);
    d->setNetmask(IPAddress::LOOPBACK_NETMASK);
    d->setMetric(1);
    ie->setIPv4Data(d);
}

//---

bool RoutingTable::isLocalAddress(const IPAddress& dest) const
{
    Enter_Method("isLocalAddress(%u.%u.%u.%u)", dest.getDByte(0), dest.getDByte(1), dest.getDByte(2), dest.getDByte(3)); // note: str().c_str() too slow here

    if (localAddresses.empty())
    {
        // collect interface addresses if not yet done
        for (int i=0; i<ift->getNumInterfaces(); i++)
        {
            IPAddress interfaceAddr = ift->getInterface(i)->ipv4Data()->getIPAddress();
            localAddresses.insert(interfaceAddr);
        }
    }

    AddressSet::iterator it = localAddresses.find(dest);
    return it!=localAddresses.end();
}

bool RoutingTable::isLocalMulticastAddress(const IPAddress& dest) const
{
    Enter_Method("isLocalMulticastAddress(%u.%u.%u.%u)", dest.getDByte(0), dest.getDByte(1), dest.getDByte(2), dest.getDByte(3)); // note: str().c_str() too slow here

    for (int i=0; i<ift->getNumInterfaces(); i++)
    {
        InterfaceEntry *ie = ift->getInterface(i);
        if (ie->ipv4Data()->isMemberOfMulticastGroup(dest))
            return true;
    }
    return false;
}


const IPRoute *RoutingTable::findBestMatchingRoute(const IPAddress& dest) const
{
    Enter_Method("findBestMatchingRoute(%u.%u.%u.%u)", dest.getDByte(0), dest.getDByte(1), dest.getDByte(2), dest.getDByte(3)); // note: str().c_str() too slow here

    RoutingCache::iterator it = routingCache.find(dest);
    if (it != routingCache.end())
        return it->second;

    // find best match (one with longest prefix)
    // default route has zero prefix length, so (if exists) it'll be selected as last resort
    const IPRoute *bestRoute = NULL;
    uint32 longestNetmask = 0;
    for (RouteVector::const_iterator i=routes.begin(); i!=routes.end(); ++i)
    {
        const IPRoute *e = *i;
        if (IPAddress::maskedAddrAreEqual(dest, e->getHost(), e->getNetmask()) &&  // match
            (!bestRoute || e->getNetmask().getInt() > longestNetmask))  // longest so far
        {
            bestRoute = e;
            longestNetmask = e->getNetmask().getInt();
        }
    }
    routingCache[dest] = bestRoute;
    return bestRoute;
}

InterfaceEntry *RoutingTable::getInterfaceForDestAddr(const IPAddress& dest) const
{
    Enter_Method("getInterfaceForDestAddr(%u.%u.%u.%u)", dest.getDByte(0), dest.getDByte(1), dest.getDByte(2), dest.getDByte(3)); // note: str().c_str() too slow here

    const IPRoute *e = findBestMatchingRoute(dest);
    return e ? e->getInterface() : NULL;
}

IPAddress RoutingTable::getGatewayForDestAddr(const IPAddress& dest) const
{
    Enter_Method("getGatewayForDestAddr(%u.%u.%u.%u)", dest.getDByte(0), dest.getDByte(1), dest.getDByte(2), dest.getDByte(3)); // note: str().c_str() too slow here

    const IPRoute *e = findBestMatchingRoute(dest);
    return e ? e->getGateway() : IPAddress();
}


MulticastRoutes RoutingTable::getMulticastRoutesFor(const IPAddress& dest) const
{
    Enter_Method("getMulticastRoutesFor(%u.%u.%u.%u)", dest.getDByte(0), dest.getDByte(1), dest.getDByte(2), dest.getDByte(3)); // note: str().c_str() too slow here here

    MulticastRoutes res;
    res.reserve(16);
    for (RouteVector::const_iterator i=multicastRoutes.begin(); i!=multicastRoutes.end(); ++i)
    {
        const IPRoute *e = *i;
        if (IPAddress::maskedAddrAreEqual(dest, e->getHost(), e->getNetmask()))
        {
            MulticastRoute r;
            r.interf = e->getInterface();
            r.gateway = e->getGateway();
            res.push_back(r);
        }
    }
    return res;
}


int RoutingTable::getNumRoutes() const
{
    return routes.size()+multicastRoutes.size();
}

const IPRoute *RoutingTable::getRoute(int k) const
{
    if (k < (int)routes.size())
        return routes[k];
    k -= routes.size();
    if (k < (int)multicastRoutes.size())
        return multicastRoutes[k];
    return NULL;
}

const IPRoute *RoutingTable::getDefaultRoute() const
{
    int n = (int)routes.size();
    for (int i=0; i<n; i++)
        if (routes[i]->getNetmask().isUnspecified())
            return routes[i];
    return NULL;
}

const IPRoute *RoutingTable::findRoute(const IPAddress& target, const IPAddress& netmask,
    const IPAddress& gw, int metric, const char *dev) const
{
    int n = getNumRoutes();
    for (int i=0; i<n; i++)
        if (routeMatches(getRoute(i), target, netmask, gw, metric, dev))
            return getRoute(i);
    return NULL;
}

void RoutingTable::addRoute(const IPRoute *entry)
{
    Enter_Method("addRoute(...)");

    // check for null address and default route
    if (entry->getHost().isUnspecified() != entry->getNetmask().isUnspecified())
        error("addRoute(): to add a default route, set both host and netmask to zero");

    if (entry->getHost().doAnd(entry->getNetmask().isUnspecified()).getInt() != 0)
        error("addRoute(): suspicious route: host %s has 1-bits outside netmask %s",
              entry->getHost().str().c_str(), entry->getNetmask().str().c_str());

    // check that the interface exists
    if (!entry->getInterface())
        error("addRoute(): interface cannot be NULL");

    // if this is a default route, remove old default route (we're replacing it)
    if (entry->getNetmask().isUnspecified() && getDefaultRoute()!=NULL)
        deleteRoute(getDefaultRoute());

    // add to tables
    if (!entry->getHost().isMulticast())
        routes.push_back(const_cast<IPRoute*>(entry));
    else
        multicastRoutes.push_back(const_cast<IPRoute*>(entry));

    if (isNeighbor(entry->getHost())) { // route to neighbor
    	routingCache[entry->getHost()] = entry;
    }

    invalidateCache();
    updateDisplayString();

    nb->fireChangeNotification(NF_IPv4_ROUTE_ADDED, entry);
}


bool RoutingTable::deleteRoute(const IPRoute *entry)
{
    Enter_Method("deleteRoute(...)");

    RouteVector::iterator i = std::find(routes.begin(), routes.end(), entry);
    if (i!=routes.end())
    {
        nb->fireChangeNotification(NF_IPv4_ROUTE_DELETED, entry); // rather: going to be deleted
        routes.erase(i);
        delete entry;
        invalidateCache();
        updateDisplayString();
        return true;
    }
    i = std::find(multicastRoutes.begin(), multicastRoutes.end(), entry);
    if (i!=multicastRoutes.end())
    {
        nb->fireChangeNotification(NF_IPv4_ROUTE_DELETED, entry); // rather: going to be deleted
        multicastRoutes.erase(i);
        delete entry;
        invalidateCache();
        updateDisplayString();
        return true;
    }
    return false;
}


bool RoutingTable::routeMatches(const IPRoute *entry,
    const IPAddress& target, const IPAddress& nmask,
    const IPAddress& gw, int metric, const char *dev) const
{
    if (!target.isUnspecified() && !target.equals(entry->getHost()))
        return false;
    if (!nmask.isUnspecified() && !nmask.equals(entry->getNetmask()))
        return false;
    if (!gw.isUnspecified() && !gw.equals(entry->getGateway()))
        return false;
    if (metric && metric!=entry->getMetric())
        return false;
    if (dev && strcmp(dev, entry->getInterfaceName()))
        return false;

    return true;
}

void RoutingTable::updateNetmaskRoutes()
{
    // first, delete all routes with src=IFACENETMASK
    for (unsigned int k=0; k<routes.size(); k++)
        if (routes[k]->getSource()==IPRoute::IFACENETMASK)
            routes.erase(routes.begin()+(k--));  // '--' is necessary because indices shift down

    // then re-add them, according to actual interface configuration
    for (int i=0; i<ift->getNumInterfaces(); i++)
    {
        InterfaceEntry *ie = ift->getInterface(i);
        if (ie->ipv4Data()->getNetmask()!=IPAddress::ALLONES_ADDRESS)
        {
            IPRoute *route = new IPRoute();
            route->setType(IPRoute::DIRECT);
            route->setSource(IPRoute::IFACENETMASK);
            route->setHost(ie->ipv4Data()->getIPAddress());
            route->setNetmask(ie->ipv4Data()->getNetmask());
            route->setGateway(IPAddress());
            route->setMetric(ie->ipv4Data()->getMetric());
            route->setInterface(ie);
            routes.push_back(route);
        }
    }

    invalidateCache();
    updateDisplayString();
}

/*
 * @param hashValue: the hash value to process (160 bit)
 * @param prevHops: a set of previous hops (to avoid cyclic routing)
 * @dest: destination end-host

 */
const IPRoute* RoutingTable::findHashRoute(std::bitset<160> &hashValue, const std::set<IPAddress> *prevHops, const IPAddress &dest)
{
	// use std::vector<bool> for space optimization
	// our assumption is that the bit-window does not take up
	// more than 16 bit (unsigned short)
	unsigned short val = 0;
	unsigned short mask = 0x1;
	unsigned tryCnt = 0;

	// Locate the edge switch for dest
	std::map<IPAddress, IPAddress>::iterator it = eSwitchMap.find(dest);
	if (it == eSwitchMap.end())
		return NULL; // endhost does not exist in the map

	IPAddress eSwitch = it->second;

	// if eSwitch == current Router ID => the destination is
	// an endhost connected to the current router
	if (eSwitch == routerId) {
		// just find the direct route to it
		const IPRoute* route = findBestMatchingRoute(dest);
		ASSERT(route != NULL);
		return route;
	}

	// Otherwise
	AddressVector *nextHopList = hashRoutes[eSwitch];
	ASSERT(nextHopList != NULL);

	// we still have bits to use
	for (unsigned i = 0; i < 160/winSize; i++) {
		unsigned start = 160 - (i + 1) * winSize;
		val = 0;
		for (unsigned j = 0; j < winSize; j++) {
			val = (val << 1);
			if (hashValue[start + j]) // bit 1
				val = val | mask; // add 1 to the righmost bit
		}

		tryCnt++;
		if (val < vlanSize) { // valid VLAN index
			IPAddress nextHop = getNextHop(nextHopList, val);
			ASSERT(nextHop != IPAddress::UNSPECIFIED_ADDRESS);

			if (!prevHops || !prevHops->count(nextHop)) { // do not create a cycle
				// find the interface entry
				const IPRoute* route = findBestMatchingRoute(nextHop);
				ASSERT(route != NULL); // shouldn't be empty
				InterfaceEntry *ie = route->getInterface();

				// check load: skip if the link is failed or highly loaded
				if (!ie->isDown() && ie->getThroughput() < threshold)
					return route;
			}
		} // skip if val > vlanSize
	}

	// no more bit to operate on: randomly pick a port
	int attempts = 0;
	int start = rand() % vlanSize;
	for (int i = 0; i < vlanSize; i++) { // only try a number of times
		IPAddress nextHop = getNextHop(nextHopList, (start + i) % vlanSize);
		if (!prevHops || !prevHops->count(nextHop)) { // do not create a cycle
			// find the interface entry
			const IPRoute* route = findBestMatchingRoute(nextHop);
			ASSERT(route != NULL); // shouldn't be empty
			InterfaceEntry *ie = route->getInterface();

			// check load: skip if the link is failed or totally congested
			if (!ie->isDown() && ie->getThroughput() < 1)
				return route;
		}
	}
	return NULL;
}

IPAddress RoutingTable::getNextHop(AddressVector *list, int k) {
	if (list == NULL)
		return IPAddress::UNSPECIFIED_ADDRESS;

	if (k >= vlanSize)
		return IPAddress::UNSPECIFIED_ADDRESS;

	return list->at(k % list->size());
}

// by Nam Dang: add next hop information of "dest" to the table
void RoutingTable::addNextHop(IPAddress dest, IPAddress nextHop)
{
	HashRoutes::iterator it = hashRoutes.find(dest);
	AddressVector *addrList;
	if (it == hashRoutes.end()) {
		addrList = new AddressVector();
		hashRoutes[dest] = addrList;
	}
	else
		addrList = it->second;

	// insert to the address set
	AddressVector::iterator addrIt = addrList->begin();
	for (; addrIt != addrList->end(); addrIt++)
		if ((*addrIt) == nextHop)
			return;

	addrList->push_back(nextHop);
}

size_t RoutingTable::getNextHopListSize(IPAddress dest)
{
	HashRoutes::iterator it = hashRoutes.find(dest);
	AddressVector *addrList = NULL;
	if (it != hashRoutes.end())
		addrList = it->second;

	if (addrList == NULL)
		return 0;

	return addrList->size();
}

void RoutingTable::addNeighbor(IPAddress addr)
{
	nbSet.insert(addr);
}

int RoutingTable::getNeighborCnt()
{
	return nbSet.size();
}

IPAddress RoutingTable::getNeighbor(int k)
{
	if (k >= (int) nbSet.size())
		return IPAddress::UNSPECIFIED_ADDRESS;

	std::set<IPAddress>::iterator it = nbSet.begin();
	while (k > 0) {
		it++;
		k--;
	}

	return *it;
}

bool RoutingTable::isNeighbor(IPAddress addr) const
{
	return (nbSet.count(addr) > 0);
}

void RoutingTable::addEdgeSwitchEntry(IPAddress dest, IPAddress edgeSwitch)
{
	eSwitchMap.insert(std::pair<IPAddress, IPAddress> (dest, edgeSwitch));
	addEdgeSwitch(edgeSwitch);
}

IPAddress RoutingTable::getEdgeSwitch(const IPAddress dest) const
{
	std::map<IPAddress, IPAddress>::const_iterator it = eSwitchMap.find(dest);
	if (it == eSwitchMap.end())
		return IPAddress::UNSPECIFIED_ADDRESS;

	return it->second;
}

void RoutingTable::dump()
{
	EV << "Dumping information of : " << routerId.str() << endl;
	EV << "Node type: " << ((isRouter_)? "Router" : "End-host") << endl;
	EV << "Bit Window Size: " << winSize << endl;

	EV << "Neighbor list: ";
	AddressSet::iterator nbIt;
	for (nbIt = nbSet.begin(); nbIt != nbSet.end(); nbIt++) {
		EV << (*nbIt).str() << " ";
	}
	EV << endl;

	EV << "Hash routing table information: " << endl;
	HashRoutes::iterator it;
	for (it = hashRoutes.begin(); it != hashRoutes.end(); it++) {
		EV << "Destination: " << (it->first).str() << endl;
		EV << "Next Hops: ";
		AddressVector *set = it->second;
		AddressVector::iterator addIt;
		for (addIt = set->begin(); addIt != set->end(); addIt++) {
			EV << (*addIt).str() << " ";
		}
		EV << endl;
	}
}

void RoutingTable::addEdgeSwitch(IPAddress node) {
	for (int i = 0; i < eSwitchList.size(); i++) {
		if (node == eSwitchList[i])
			return;
	}

	eSwitchList.push_back(node);
}

int RoutingTable::getNumEdgeSwitch() const {
	return eSwitchList.size();
}

IPAddress RoutingTable::getEdgeSwitchAt(int idx) const {
	if (idx < 0 || idx > eSwitchList.size())
		return IPAddress::UNSPECIFIED_ADDRESS;

	return eSwitchList[idx];
}

int RoutingTable::getEdgeSwitchIdx(IPAddress eSwitch) const
{
	for (int i = 0; i < eSwitchList.size(); i++) {
		if (eSwitch == eSwitchList[i])
			return i;
	}

	return -1;
}

bool RoutingTable::isEndHost(IPAddress node) const {
	std::map<IPAddress, IPAddress>::const_iterator it = eSwitchMap.find(node);
	if (it == eSwitchMap.end())
		return false;

	return true;
}
